#pragma once
#include <array>
#include "../../vector/Vector.h"
#include "../function/Memory.hpp"

struct TraceHitboxData_t
{
public:
	char pad_01[0x38];
	int m_nHitGroup;
	char pad_02[0x4];
	int m_nHitboxId;
};
static_assert(sizeof(TraceHitboxData_t) == 0x44);

class CTraceArrayElement 
{
	char pad[0x30];
public:
};

class CTraceRay
{
public:
	char Pad[0x80];
};

class CTraceFilter
{
	char pad_01[0x8];
public:
	int64_t nTraceMask;
	int64_t arrUnknown[2];
	int32_t arrSkipHandles[4];
	int16_t arrCollisions[7];
	int16_t nUnknown2;
	uint8_t nUnknown3;
	uint8_t nUnknown4;
	uint8_t nUnknown5;
};

class CGameTrace 
{
public:
	int GetHitboxId();
	int GetHitgroup();

	void* pSurface;
	void* pHitEntity;
	TraceHitboxData_t* pHitboxData;
	char pad_01[0x38];
	uint32_t nContents;
	char pad_02[0x24];
	Vector3 vecStart;
	Vector3 vecEnd;
	Vector3 vecNormal;
	Vector3 vecPosition;
	char pad_03[0x4];
	float flFraction;
	char pad_04[0x6];
	bool bStartSolid;
	char pad_05[0x4D];

	bool DidHit() const noexcept {
		return (this->flFraction < 1.0f || bStartSolid);
	}

	bool IsVisible() const noexcept {
		return this->flFraction > 0.9f;
	}
};

class CTraceInfo 
{
public:
	float flPpreviousLengthModifier;
	float flCurrentLengthModifier;
	float flDamage;
	std::uint32_t nPenetrationCount;
	uint32_t hEntity;
	char pad[4];
};

class CTraceData 
{
public:
	int32_t nUnknown1;
	float flUnknown2 = 52.f;
	void* pArray;
	int32_t nUnknown3 = 128;
	int32_t nUnknown4 = static_cast<std::int32_t>(0x80000000);
	std::array<CTraceArrayElement, 0x80> arrTrace = {};
	char pad[0x8];
	int32_t nPenetrationCount;
	CTraceInfo* pTraceInfo;
	char pad2[0x8];
	void* pUnknown;
	char pad3[0xB8];
	Vector3 vecStart;
	Vector3 vecEnd;
	char pad4[0x50];

	void Release() {
		static void* pMemAlloc = *reinterpret_cast<void**>(Memory::GetExport("tier0.dll", "g_pMemAlloc"));
		if (pUnknown) {
			Memory::CallClassFn<void, 3>(pMemAlloc, pUnknown);
		}

		if (pTraceInfo) {
			Memory::CallClassFn<void, 3>(pMemAlloc, pTraceInfo);
		}
	}
};

namespace Ray
{
	void InitFilter(CTraceFilter* pFilter, void* pEntity, uint64_t nMask, uint8_t nLayer1, uint16_t nLayer2);
	CGameTrace TraceShape(Vector3& vecStart, Vector3& vecEnd, uintptr_t pSkipEntity, const uint64_t nMask);
}

enum contents_t {
	CONTENTS_EMPTY = 0,
	CONTENTS_SOLID = 0x1,
	CONTENTS_WINDOW = 0x2,
	CONTENTS_AUX = 0x4,
	CONTENTS_GRATE = 0x8,
	CONTENTS_SLIME = 0x10,
	CONTENTS_WATER = 0x20,
	CONTENTS_BLOCKLOS = 0x40,
	CONTENTS_OPAQUE = 0x80,
	CONTENTS_TESTFOGVOLUME = 0x100,
	CONTENTS_UNUSED = 0x200,
	CONTENTS_BLOCKLIGHT = 0x400,
	CONTENTS_TEAM1 = 0x800,
	CONTENTS_TEAM2 = 0x1000,
	CONTENTS_IGNORE_NODRAW_OPAQUE = 0x2000,
	CONTENTS_MOVEABLE = 0x4000,
	CONTENTS_AREAPORTAL = 0x8000,
	CONTENTS_PLAYERCLIP = 0x10000,
	CONTENTS_MONSTERCLIP = 0x20000,
	CONTENTS_CURRENT_0 = 0x40000,
	CONTENTS_CURRENT_90 = 0x80000,
	CONTENTS_CURRENT_180 = 0x100000,
	CONTENTS_CURRENT_270 = 0x200000,
	CONTENTS_CURRENT_UP = 0x400000,
	CONTENTS_CURRENT_DOWN = 0x800000,
	CONTENTS_ORIGIN = 0x1000000,
	CONTENTS_MONSTER = 0x2000000,
	CONTENTS_DEBRIS = 0x4000000,
	CONTENTS_DETAIL = 0x8000000,
	CONTENTS_TRANSLUCENT = 0x10000000,
	CONTENTS_LADDER = 0x20000000,
	CONTENTS_HITBOX = 0x40000000,
};

enum trace_masks_t {
	MASK_ALL = 0xFFFFFFFF,
	MASK_SOLID = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE,
	MASK_PLAYERSOLID = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE,
	MASK_NPCSOLID = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE,
	MASK_NPCFLUID = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE,
	MASK_WATER = CONTENTS_WATER | CONTENTS_MOVEABLE | CONTENTS_SLIME,
	MASK_OPAQUE = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE,
	MASK_OPAQUE_AND_NPCS = MASK_OPAQUE | CONTENTS_MONSTER,
	MASK_BLOCKLOS = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_BLOCKLOS,
	MASK_BLOCKLOS_AND_NPCS = MASK_BLOCKLOS | CONTENTS_MONSTER,
	MASK_VISIBLE = MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE,
	MASK_VISIBLE_AND_NPCS = MASK_OPAQUE_AND_NPCS | CONTENTS_IGNORE_NODRAW_OPAQUE,
	MASK_SHOT = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTER | CONTENTS_WINDOW | CONTENTS_DEBRIS | CONTENTS_GRATE | CONTENTS_HITBOX,
	MASK_SHOT_BRUSHONLY = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_DEBRIS,
	MASK_SHOT_HULL = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTER | CONTENTS_WINDOW | CONTENTS_DEBRIS | CONTENTS_GRATE,
	MASK_SHOT_PORTAL = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER,
	MASK_SOLID_BRUSHONLY = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_GRATE,
	MASK_PLAYERSOLID_BRUSHONLY = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_PLAYERCLIP | CONTENTS_GRATE,
	MASK_NPCSOLID_BRUSHONLY = CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP | CONTENTS_GRATE,
	MASK_NPCWORLDSTATIC = CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP | CONTENTS_GRATE,
	MASK_NPCWORLDSTATIC_FLUID = CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP,
	MASK_SPLITAREPORTAL = CONTENTS_WATER | CONTENTS_SLIME,
	MASK_CURRENT = CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | CONTENTS_CURRENT_UP | CONTENTS_CURRENT_DOWN,
	MASK_DEADSOLID = CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_GRATE,
};